package com.pro.common.file.storage.huawei.service;

import com.obs.services.ObsClient;
import com.obs.services.exception.ObsException;
import com.obs.services.model.*;
import com.pro.common.api.service.ITestService;
import com.pro.common.api.statics.user.file.storage.enums.EnumFileStoragePlatform;
import com.pro.common.api.statics.user.file.storage.model.dto.UploadFIleResultDTO;
import com.pro.common.file.storage.config.FileStorageProperties;
import com.pro.common.file.storage.huawei.config.ObsProperties;
import com.pro.common.file.storage.huawei.config.ObsPropertiesContainer;
import com.pro.common.file.storage.service.AbstractFileStorageService;
import com.pro.framework.api.file.storage.PresignedUrlResponse;
import com.pro.framework.api.util.CollUtils;
import com.pro.framework.api.util.JSONUtils;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Huawei OBS 存储实现
 */
@Slf4j
@AllArgsConstructor
@Service
@Getter
public class FileStorageServiceHuaweiObs extends AbstractFileStorageService<ObsClient, ObsPropertiesContainer, ObsProperties> implements ITestService {
    //    private ObsClient obsClient;
//    private ObsProperties props;
    private FileStorageProperties fileStorageProperties;
//    private final JsonServiceUnderline jsonService;

    private ObsPropertiesContainer propertiesList;
    private final EnumFileStoragePlatform platform = EnumFileStoragePlatform.huawei_obs;


    // ====================== 预签名 ======================
    @Override
    public PresignedUrlResponse generatePresignedUploadUrl(String bucket, String objectKey, long expireSeconds, String contentType) {
        try {
            HttpMethodEnum method = HttpMethodEnum.PUT;
            TemporarySignatureRequest request = new TemporarySignatureRequest(method, expireSeconds);

            // 修正：不要去掉第一个字符，除非有特殊原因
            String normalizedObjectKey = objectKey.startsWith("/") ? objectKey.substring(1) : objectKey;
            request.setObjectKey(normalizedObjectKey);

            // 重要：设置 Content-Type 头，避免签名不匹配
            Map<String, String> headers = new HashMap<>();
            headers.put("Content-Type", contentType); // 设置默认 Content-Type  image/png
            request.setHeaders(headers);
            request.setBucketName(bucket);
            TemporarySignatureResponse response = getClientAndProps(bucket).getClient().createTemporarySignature(request);

            return new PresignedUrlResponse(
                    response.getSignedUrl(),
                    method.name(),
//                    headers, // 返回 headers，让前端知道要设置什么
                    expireSeconds
            );
        } catch (Exception e) {
            log.error("生成上传预签名URL失败: bucket={}, objectKey={}", bucket, objectKey, e);
            throw new RuntimeException("生成上传URL失败", e);
        }
    }


    @Override
    public PresignedUrlResponse generatePresignedDownloadUrl(String bucket, String objectKey, long expireSeconds) {
        try {
            HttpMethodEnum method = HttpMethodEnum.GET; // 下载应该是 GET
            TemporarySignatureRequest request = new TemporarySignatureRequest(method, expireSeconds);


            // 修正：统一对象键处理逻辑
            String normalizedObjectKey = objectKey.startsWith("/") ? objectKey.substring(1) : objectKey;
            request.setObjectKey(normalizedObjectKey);

            TemporarySignatureResponse response = getClientAndProps(bucket).getClient().createTemporarySignature(request);

            return new PresignedUrlResponse(
                    response.getSignedUrl(),
                    method.name(),
//                    Map.of(),
                    expireSeconds
            );
        } catch (Exception e) {
            log.error("生成下载预签名URL失败: bucket={}, objectKey={}", bucket, objectKey, e);
            throw new RuntimeException("生成下载URL失败", e);
        }
    }

    @Override
    public PresignedUrlResponse generatePresignedUploadUrlWithCallback(String bucket, String objectKey, long expireSeconds, Map<String, String> customVariables) {
        Map<String, String> baseVariables = new HashMap<>(customVariables);
        baseVariables.put("bucket", "$(bucket)");
        baseVariables.put("object", "$(key)");
        baseVariables.put("size", "$(size)");
        baseVariables.put("etag", "$(etag)");
        baseVariables.put("width", "$(imageInfo.width)");
        baseVariables.put("height", "$(imageInfo.height)");
        String callbackUrl = fileStorageProperties.getBaseUrl() + "/upload/callback/" + EnumFileStoragePlatform.huawei_obs.name();
        String callbackBody = JSONUtils.toString(baseVariables);
//        String callbackBody = "{\"bucket\":\"$(bucket)\",\"object\":\"$(key)\",\"size\":\"$(size)\",\"etag\":\"$(etag)\",\"userId\":\"$(x:userId)\"}";
        String callbackBodyType = "application/json";
        // 检查重复
        for (String key : customVariables.keySet()) {
            if (baseVariables.containsKey(key)) {
                // 打印日志或警告
                log.warn("Warning: custom variable {} overlaps with base variable", key);
            }
        }

        TemporarySignatureRequest request = new TemporarySignatureRequest(HttpMethodEnum.PUT, expireSeconds);

        request.setObjectKey(objectKey.substring(1));

        // Example: 设置 Content-Type header
//        Map<String, String> headers = new HashMap<>();
//        headers.put("Content-Type", "application/octet-stream");
//        request.setHeaders(headers);

        // 构造回调策略 JSON
        String cbJson = String.format(
                "{\"callbackUrl\":\"%s\",\"callbackBody\":%s,\"callbackBodyType\":\"%s\"}",
                callbackUrl,
                callbackBody,
                callbackBodyType
        );
        String base64Cb = Base64.getEncoder().encodeToString(cbJson.getBytes(StandardCharsets.UTF_8));

        Map<String, Object> qp = new HashMap<>();
        qp.put("x-obs-callback", base64Cb);
        request.setQueryParams(qp);

        TemporarySignatureResponse response = getClientAndProps(bucket).getClient().createTemporarySignature(request);
        String signedUrl = response.getSignedUrl();

        return new PresignedUrlResponse(
                signedUrl,
                "PUT",
//                null,
                expireSeconds
//                customVariables
        );
    }

    // ====================== 上传 ======================
    @Override
    @SneakyThrows
    public UploadFIleResultDTO putObject(String bucket, String objectKey, InputStream inputStream) {
        // 注意：不要在这里 try(inputStream)，流的生命周期由调用方管理
        try {
            PutObjectResult result = getClientAndProps(bucket).getClient().putObject(bucket, objectKey, inputStream);

            if (result != null && result.getObjectUrl() != null && !result.getObjectUrl().isEmpty()) {
                String etag = result.getEtag();
                if (etag != null && etag.startsWith("\"") && etag.endsWith("\"")) {
                    etag = etag.substring(1, etag.length() - 1);
                }
                return new UploadFIleResultDTO(result.getObjectUrl(), etag);
            }

            return null;
        } catch (ObsException e) {
            log.error("OBS putObject failed: bucket={}, key={}, errorCode={}, errorMessage={}",
                    bucket, objectKey, e.getErrorCode(), e.getErrorMessage(), e);
            throw e;
        }
    }

    private String normalizeEndpoint(String endpoint) {
        if (endpoint == null) return "";
        String e = endpoint.trim();
        e = e.replaceFirst("^https?://", "");    // 去掉 http(s)://
        if (e.endsWith("/")) e = e.substring(0, e.length() - 1); // 去掉末尾斜杠
        return e;
    }

    private String encodePath(String key) {
        if (key == null || key.isEmpty()) return "";
        return Arrays.stream(key.split("/"))
                .map(seg -> URLEncoder.encode(seg, StandardCharsets.UTF_8))
                .collect(Collectors.joining("/"));
    }

    @Override
    public Map<String, String> putObjectsFromUrls(String bucket, String objectKeyPrefix, List<String> sourceUrls) {
        Map<String, String> resultMap = new HashMap<>();
        for (String sourceUrl : sourceUrls) {
            try (InputStream in = new java.net.URL(sourceUrl).openStream()) {
                String objectKey = objectKeyPrefix + UUID.randomUUID();
                this.putObject(bucket, objectKey, in);
                resultMap.put(objectKey, "success");
            } catch (Exception e) {
                resultMap.put(sourceUrl, "failed: " + e.getMessage());
            }
        }
        return resultMap;
    }

    // ====================== 下载 ======================

    @Override
    public InputStream getObject(String bucket, String objectKey) {
        ObsObject obsObject = getClientAndProps(bucket).getClient().getObject(bucket, objectKey);
        return obsObject.getObjectContent();
    }

    // ====================== 删除 ======================

    @Override
    public boolean deleteObject(String bucket, String objectKey) {
        try {
            DeleteObjectResult rs = getClientAndProps(bucket).getClient().deleteObject(bucket, objectKey);
            return true;
        } catch (ObsException e) {
            log.error("OBS deleteObject failed: bucket={}, key={}", bucket, objectKey, e);
            return false;
        }
    }

    @Override
    public List<String> deleteObjects(String bucket, List<String> objectKeys) {
        return CollUtils.partitionList(objectKeys, 500).stream().flatMap(list -> {
            DeleteObjectsRequest req = new DeleteObjectsRequest(bucket);
            for (String objectKey : objectKeys) {
                req.addKeyAndVersion(objectKey);
            }
            DeleteObjectsResult result = null;
            try {
                result = getClientAndProps(bucket).getClient().deleteObjects(req);
                return result.getDeletedObjectResults().stream().map(DeleteObjectsResult.DeleteObjectResult::getObjectKey);
            } catch (ObsException e) {
                log.error("deleteObjects error: {} {} {} ", bucket, objectKeys, e.getMessage(), e);
                return Stream.empty();
//                throw e;
            }
        }).toList();
    }

    // ====================== 查询 ======================

    @Override
    public boolean doesObjectExist(String bucket, String objectKey) {
        try {
            return getClientAndProps(bucket).getClient().doesObjectExist(bucket, objectKey);
        } catch (ObsException e) {
            return false;
        }
    }

    @Override
    public Map<String, Object> getObjectMetadata(String bucket, String objectKey) {
        ObjectMetadata metadata = getClientAndProps(bucket).getClient().getObjectMetadata(bucket, objectKey);
        Map<String, Object> map = new HashMap<>();
        map.put("contentLength", metadata.getContentLength());
        map.put("contentType", metadata.getContentType());
        map.put("etag", metadata.getEtag());
        map.put("lastModified", metadata.getLastModified());
        return map;
    }

    @Override
    public List<String> listObjects(String bucket, String prefix, String marker, int maxKeys) {
        ListObjectsRequest request = new ListObjectsRequest(bucket);
        request.setPrefix(prefix);       // 前缀
        request.setMarker(marker);       // 起始标记
        request.setMaxKeys(maxKeys);     // 分页数量

        ObjectListing listing = getClientAndProps(bucket).getClient().listObjects(request);

        List<String> keys = new ArrayList<>();
        listing.getObjects().forEach(obj -> keys.add(obj.getObjectKey()));

        return keys;
    }


    // ====================== 桶管理 ======================

    @Override
    public boolean createBucket(String bucket) {
        try {
            getClientAndProps(bucket).getClient().createBucket(bucket);
            return true;
        } catch (ObsException e) {
            log.error("OBS createBucket failed: {}", bucket, e);
            return false;
        }
    }

    @Override
    public boolean deleteBucket(String bucket) {
        try {
            getClientAndProps(bucket).getClient().deleteBucket(bucket);
            return true;
        } catch (ObsException e) {
            log.error("OBS deleteBucket failed: {}", bucket, e);
            return false;
        }
    }

    @Override
    public boolean doesBucketExist(String bucket) {
        return getClientAndProps(bucket).getClient().headBucket(bucket);
    }

    @Override
    public String getObjectUrlPublic(String bucket, String objectKey) {
        String host = normalizeEndpoint(getClientAndProps(bucket).getProperties().getEndpoint());
        String safeKey = encodePath(objectKey);

        return String.format("https://%s.%s%s", bucket, host, safeKey);
    }

    @Override
    public Object uploadCallback(Map<String, String> headers, Map<String, Object> paramMap, String body) {
        return null;
    }

    @Override
    public void test() {
        PresignedUrlResponse response = generatePresignedUploadUrl(null, "/test/" + System.currentTimeMillis(), 600, null);
        System.out.println(JSONUtils.toString(response.getUrl()));
    }


    @Override
    protected ObsClient createClient(ObsProperties props) {
        return new ObsClient(props.getAccessKey(), props.getSecretKey(), props.getEndpoint());
    }
}
